/*
 * Copyright (c)  2008 Sebastien Tremblay
 *
 * id3-tag-builder - classes.Path.java
 *
 * All source and documentation is copyrighted by Sebastien Tremblay
 * (seb at ryders dot net) and made available under the Apache License 2.0.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Id: Path.java 32 2010-03-31 13:23:02Z ryders $
 */
package com.empireone.id3tagbuilder.classes;

import com.empireone.id3tagbuilder.helpers.FoldersFilter;
import com.empireone.id3tagbuilder.helpers.RegexFilter;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * The <code>Path</code> class is the representation of a (root) music
 * library/album/artist on disc, within a given path.
 *
 * @author Sebastien Tremblay
 */
public class Path implements java.io.Serializable {

    private short id;
    private String name;
    private short depth;
    private String uri;
    private String root;
    private File file;          // used to store the file pointer once created
    private List<Path> children;

    public Path(short id, String name, String uri, short depth) {

        this.id = id;
        this.depth = depth;
        setUri(uri);
        setRoot(uri);
        setName(name);
    }

    private File getHandle() {
        if (file == null) {
            file = new File(uri);
        }
        return file;
    }

    private void fillCache() {

        File f = getHandle();

        if (f.isFile()) {
            children = new ArrayList<Path>(0);
        } else {
            String[] files = f.list(new RegexFilter(".*\\.[Mm][Pp]3"));
            String[] childs = f.list(FoldersFilter.getInstance());

            int size = (files != null ? files.length : 0) + (childs != null ? childs.length : 0);
            children = new ArrayList<Path>(size);

            if (childs != null) {
                // all childs are directories
                Arrays.sort(childs, String.CASE_INSENSITIVE_ORDER);
                for (String child : childs) {
                    children.add(new Path(null, uri + System.getProperty("file.separator") + child, root, (short) (depth - 1)));
                }
            }
            if (files != null) {
                Arrays.sort(files, String.CASE_INSENSITIVE_ORDER);

                for (String _file : files) {
                    children.add(new Path(null, uri + System.getProperty("file.separator") + _file, root, (short) (depth - 1)));
                }
            }
        }
    }

    /**
     * Builds a <code>Path</code> based on the name and uri given. Object
     * will be valid if the <code>uri</code> points to and existing folder.
     * @param           name of the path object to display
     * @param           uri for this path (folder location)
     */
    public Path(String name, String uri, String root, short depth) {
        this.depth = depth;
        setUri(uri);
        setName(name);
        setRoot(root);
    }

    /**
     *
     * @param name
     */
    public void setName(String name) {
        this.name = name == null ? "" : name;

        if (this.name.equals("") && uri != null) {
            this.name = uri.substring(uri.lastIndexOf(System.getProperty("file.separator")) + 1);
        }
    }

    public void setRoot(String root) {
        this.root = root;
        if (root.endsWith(System.getProperty("file.separator"))) {
            root = root.substring(0, root.length() - 1);
        }
        checkRoot();
    }

    public void setUri(String uri) {
        this.uri = uri;
        uri = uri.substring(0, uri.length() - 1);
        checkRoot();
    }

    private void checkRoot() {
        if (depth > 0 && uri != null && root != null && !root.equals(uri)) {

            if (uri.lastIndexOf(System.getProperty("file.separator")) == root.length()) {
                root = uri;
            } else {
                root = uri.substring(0, uri.indexOf(System.getProperty("file.separator"), root.length() - 1));
            }
        }
    }

    /**
     * Builds a <code>Path</code> based on the name and uri given. Object
     * will be valid if the <code>uri</code> points to and existing folder.
     * @param           id : unique identifier
     * @param           name of the path object to display
     * @param           uri for this path (folder location)
     */
    public Path(short id, String name, String uri) {
        this.id = id;
        this.name = name;
        setRoot(uri);
        setUri(uri);
    }

    /**
     *
     * @param name
     * @param children
     */
    public Path(String name, List<Path> children) {

        this.name = name;
        this.children = children;
    }

    /**
     * @return          the unique identifier for this Path, otherwise -1
     */
    public short getId() {
        return this.id > 0 ? this.id : -1;
    }

    /**
     * @return          the file pointer to the current Path folder.
     */
    protected File getFile() {
        if (file == null) {
            file = new File(getUri());
        }
        return file;
    }

    /***
     * Checks whether this path is valid. To be so, a path must exist
     * and be a directory.
     * @return          bool : whether this is a valid (usable) Path
     */
    public boolean isValid() {

        return getFile().exists() && getFile().isDirectory();
    }

    /***
     * @return          a <code>File</code> of all child files in the Path
     *                  mapped folder. Note that no filter is applied.
     */
    public File[] listFiles() {

        File[] res = getFile().listFiles();

        if (res == null) {
            res = new File[0];
        }

        return res;
    }

    /**
     * @return          the Path name to be displayed
     */
    public String getName() {
        return name;
    }

    /**
     * @return          the uri to the physical folder for this Path
     */
    public String getUri() {
        return uri;
    }

    /**
     *
     * @param index
     * @return
     */
    public Path getChild(int index) {
        if (children == null) {
            fillCache();
        }
        return children.get(index);
    }

    /**
     *
     * @return
     */
    public int getChildCount() {
        if (children == null) {
            fillCache();
        }
        return children.size();
    }

    public List<Path> getChildren() {
        if (children == null) {
            fillCache();
        }
        return this.children;
    }

    /**
     *
     * @return
     */
    @Override
    public String toString() {
        return name;
    }

    public String getRoot() {
        return root;
    }

    public short getDepth() {
        return depth;
    }
}
